/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "ParticleSystem.h"
#include <logs/Log.h>
#include <algorithm>
#include <lib3d/material/Texture.h>
#include <xml/Saver.h>

// initializers
#include "initializers/BaseRandomInitializer.h"
#include "initializers/SizeInitializer.h"

// events
#include "events/VelocityEvent.h"
#include "events/AlphaEvent.h"
#include "events/ColorEvent.h"
#include "events/GravityEvent.h"
#include "events/SizeEvent.h"
#include "events/TorqueEvent.h"
#include "events/RotationEvent.h"

#define TryParseInitializer(NodeClass, NodeName)\
if(name == NodeName)\
{\
	NodeClass * newInitializer = new NodeClass();\
	if(!provider->parse_object(newInitializer))\
	{\
		newInitializer->release();\
		return false;\
	}\
	add_particles_initializer(newInitializer);\
	newInitializer->release();\
	return true;\
}

#define TryParseEvent(NodeClass, NodeName)\
if(name == NodeName)\
{\
	NodeClass * newEvent = new NodeClass();\
	if(!provider->parse_object(newEvent))\
	{\
		newEvent->release();\
		return false;\
	}\
	add_particles_event(newEvent->get_time(), newEvent);\
	newEvent->release();\
	return true;\
}

namespace liba
{
	namespace lib3d
	{
		namespace particles
		{
			class FrameLoader : public xml::ObjectANSI
			{
			public:
				math::Rect<float> rect;
				virtual bool on_attribute(xml::Provider * provider, const std::string & name, const std::string & value)
				{
					if(name == "Value")
						return converter::convert(value, &rect);
					
					return false;
				}
			};

			class FrameSaver : public xml::ObjectANSI
			{
			public:
				math::Rect<float> rect;
				FrameSaver(const math::Rect<float> & rect) : rect(rect)
				{
				}
				virtual void on_save_attributes(xml::SaverBase * saver) const
				{
					saver->save_attribute("Value", rect);
				}
			};
			
			ParticleSystem::ParticleSystem(const std::string & name) : _referencesCounter(1), _name(name), _particlesLifetime(0), _emitterLifetime(0), 
																  _maxParticlesCount(0), _emissionRate(0), _emissionCount(0), _frameDelayTime(0.03), 
																  _framesExtracted(false), _activate(true), _useAtlas(false),
																  _blendMode(hard::BM_ALPHA), _parentToEmitter(true)
			{
				hard::Manager::add_dependent(this);
			}
			
			ParticleSystem * ParticleSystem::retain()
			{
				++_referencesCounter;
				
				return this;
			}
			void ParticleSystem::release()
			{
				if(--_referencesCounter == 0)
					delete this;
			}
			
			ParticleSystem::~ParticleSystem()
			{
				// remove from hardware dependent object list
				hard::Manager::remove_dependent(this);
				
				// release all initializers
				std::vector<ParticleInitializer*>::const_iterator itr = _initializers.begin();
				while(itr != _initializers.end())
				{
					// release current initializer
					(*itr)->release();
					
					// next object
					++itr;
				}
				
				// release all events
				std::map<Atom, KeyFrameArray>::iterator itr2 = _keyFrames.begin();
				while(itr2 != _keyFrames.end())
				{
					// getting frames array for current event's type
					KeyFrameArray * keyFrames = &itr2->second;
					
					// iterate over all keyframes
					KeyFrameIterator itr3 = keyFrames->begin();
					while(itr3 != keyFrames->end())
					{
						// release event
						itr3->event->release();
						
						// next object
						++itr3;
					}
					
					// next event type
					++itr2;
				}
			}
			
			void ParticleSystem::on_add_hardware(hard::Manager::hardware_type * hardware)
			{
				hard_vector.push_back(SweetPair(hardware, static_cast<hard::Texture*>(NULL)));
			}
			
			void ParticleSystem::set_particles_lifetime(Float newLifetime)
			{
				_particlesLifetime = newLifetime;
			}
			
			Float ParticleSystem::get_particles_lifetime()
			{
				return _particlesLifetime;
			}
			
			void ParticleSystem::set_emitter_lifetime(Float newLifetime)
			{
				_emitterLifetime = newLifetime;
			}
			
			Float ParticleSystem::get_emitter_lifetime()
			{
				return _emitterLifetime;
			}
			
			void ParticleSystem::set_max_particles_count(size_t maxParticles)
			{
				_maxParticlesCount = maxParticles;
			}
			
			size_t ParticleSystem::get_max_particles_count()
			{
				return _maxParticlesCount;
			}
			
			void ParticleSystem::set_emission_rate(Float emissionRate)
			{
				_emissionRate = emissionRate;
			}
			
			Float ParticleSystem::get_emission_rate()
			{
				return _emissionRate;
			}
			
			void ParticleSystem::set_emission_count(size_t emissionCount)
			{
				_emissionCount = emissionCount;
			}
			
			size_t ParticleSystem::get_emission_count()
			{
				return _emissionCount;
			}

			void ParticleSystem::set_blend_mode(hard::BlendMode value)
			{
				this->_blendMode = value;
			}

			hard::BlendMode ParticleSystem::get_blend_mode() const
			{
				return this->_blendMode;
			}

			inline bool portable_isnan(float value)
			{
				volatile float f = value;
				return f != f;
			}
			
			void ParticleSystem::get_texture_coords_for_time(Float time, math::Vector2<Float> * minCorner, math::Vector2<Float> * maxCorner)
			{
				// if we have only one frame - return full texture rect
				if(_textureFrames.size() == 0)
				{
					*minCorner = math::Vector2<Float>(0, 0);
					*maxCorner = math::Vector2<Float>(1, 1);
					return;
				}
				
				// check if we need extract frames
				if(!_framesExtracted)
				{
					// getting texture
					hard::Texture * texture = hard_vector.begin()->second;
					
					// if texture doesn't loaded yet - just exit
					if(texture == NULL)
						return;
					
					// getting texture size
					Float width  = texture->get_width();
					Float height = texture->get_height();
					
					if(_useAtlas)
					{
						for(unsigned int i = 0; i < _textureFrames.size(); ++i)
						{
							_textureFrames[i].minCorner = math::Vector2<Float>(_textureFrames[i].minCorner.x / width, _textureFrames[i].minCorner.y / height);
							_textureFrames[i].maxCorner = math::Vector2<Float>(_textureFrames[i].maxCorner.x / width, _textureFrames[i].maxCorner.y / height);
						}
					}
					else
					{
					// compute frames in one texture's strip
					int framesInStrip = width / _frameSize.x;
					
					// extract frames rects
					for(unsigned int i = 0; i < _textureFrames.size(); ++i)
					{
						// compute upper-left corner of rect
						Float x = (i % framesInStrip) * _frameSize.x;
						Float y = (i / framesInStrip) * _frameSize.y;
						
						// setup rect
						_textureFrames[i].minCorner = math::Vector2<Float>(x                  / width, y                  / height);
						_textureFrames[i].maxCorner = math::Vector2<Float>((x + _frameSize.x) / width, (y + _frameSize.y) / height);
					}
					}
					// now all frames are extracted
					_framesExtracted = true;
				}
				
				// compute frame
				int frame = int(time / _frameDelayTime) % _textureFrames.size();
				
				// getting rect for this frame
				FrameTextureCoords * coords = &_textureFrames[frame];
				
				// setup values
				*minCorner = coords->minCorner;
				*maxCorner = coords->maxCorner;
			}
			
			hard::Texture * ParticleSystem::get_particles_texture(hard::Hardware * hardware)
			{
				// find in all hardwares
				HardVector::iterator it = hard_vector.begin();
				for(; it != hard_vector.end(); ++it)
				{
					// if we found required hardware
					if(it->first == hardware)
					{
						// if texture doesn't loaded yet - load it
						if(it->second == NULL)
						{
							load_texture(it);
							return NULL;
						}
						
						// return texture
						return it->second;
					}
				}
				
				// nothing found for this hardware
				return NULL;
			}
			
			void ParticleSystem::load_texture(HardVector::iterator texture)
			{
				// validate texture name
				if(_texturePath == "")
					return;
				
				// if hardware available
				if(!texture->first->is_operational())
					return;
				
				// create picture loader
				if(_pictureLoader[texture->first] == NULL)
					_pictureLoader[texture->first] = new pictureformat::Loader(_texturePath.c_str());
				
				// wait picture loader
				if(!_pictureLoader[texture->first]->is_ready())
					return;
				
				// check if it valid
				if(!_pictureLoader[texture->first]->is_valid())
					THROW(GluckBadResource(__FUNCTION__, "Picture loader is not valid", _texturePath.c_str()));
				
				// getting count of mip-level in texture
				int mipmapLevels = material::Texture::get_mipmap_levels(_pictureLoader[texture->first]);
				
				// create texture
				if(!texture->second)
					texture->second = texture->first->create_texture(mipmapLevels, _pictureLoader[texture->first]->get_best_format(), 
																	 _pictureLoader[texture->first]->get_width(), _pictureLoader[texture->first]->get_height());
				
				// check if it valid
				if(!texture->second->is_valid())
				{
					// can be created less mipmaps, then requested
					for(int mip = 0; mip != texture->second->get_mipmap_levels(); ++mip)
					{
						
						// lock current mip-level of texture
						int stride = 0;
						void * destinationData = texture->second->lock(mip, &stride);
						
						// validate data
						if(!destinationData)
							THROW(GluckBadResource(__FUNCTION__, "Picture data is not valid", _texturePath.c_str()));
						
						// reset current mip-level
						_pictureLoader[texture->first]->reset(texture->second->get_hardware_format(), mip);
						
						// pack all scanlines to the current texture mip-level
						for(int i = 0; i != _pictureLoader[texture->first]->get_height(mip); ++i)
						{
							_pictureLoader[texture->first]->unpack_scanline(destinationData);
							destinationData = reinterpret_cast<char*>(destinationData) + stride;
						}
						
						// unlock current texture mip-level
						texture->second->unlock(mip);
					}
				}
				
				// release picture loader
				delete _pictureLoader[texture->first];
				_pictureLoader[texture->first] = NULL;
			}
			
			void ParticleSystem::add_particles_initializer(ParticleInitializer * initializer)
			{
				// add initializer to the array
				_initializers.push_back(initializer->retain());
			}
			
			void ParticleSystem::add_event_type(const Atom & typeName)
			{
				// check if event type already handled by particle system
				if(_keyFrames.find(typeName) != _keyFrames.end())
					return;
				
				// add keyframes array for this type
				_keyFrames[typeName] = KeyFrameArray();
			}

			void ParticleSystem::clear_particles_events()
			{
				KeyFrameMap::iterator itr = _keyFrames.begin();
				while(itr != _keyFrames.end())
				{
					// remove all events for this type
					KeyFrameIterator itr2 = itr->second.begin();
					while(itr2 != itr->second.end())
					{
						// remove event
						if((*itr2).event != NULL)
							(*itr2).event->release();

						itr2++;
					}

					// next
					itr++;
				}

				// clear event types
				_keyFrames.clear();
			}

			size_t ParticleSystem::get_particles_initializers_count()
			{
				return _initializers.size();
			}

			ParticleInitializer * ParticleSystem::get_particles_initializer(size_t index)
			{
				if(index >= _initializers.size())
					return NULL;

				return _initializers[index];
			}

			size_t ParticleSystem::get_particles_events_count() const
			{
				size_t result = 0;

				KeyFrameMap::const_iterator itr = _keyFrames.begin();
				while(itr != _keyFrames.end())
				{
					result += itr->second.size();
					itr++;
				}

				return result;
			}

			ParticleEvent * ParticleSystem::get_particles_event(size_t index) const
			{
				size_t current = 0;

				KeyFrameMap::const_iterator itr = _keyFrames.begin();
				while(itr != _keyFrames.end())
				{
					KeyFrameArray::const_iterator itr2 = itr->second.begin();
					while(itr2 != itr->second.end())
					{
						if(current == index)
							return itr2->event;

						current++;
						itr2++;
					}

					itr++;
				}

				return NULL;
			}

			float ParticleSystem::get_particles_event_time(size_t index) const
			{
				size_t current = 0;

				KeyFrameMap::const_iterator itr = _keyFrames.begin();
				while(itr != _keyFrames.end())
				{
					KeyFrameArray::const_iterator itr2 = itr->second.begin();
					while(itr2 != itr->second.end())
					{
						if(current == index)
							return itr2->time;

						current++;
						itr2++;
					}

					itr++;
				}

				return NULL;
			}
			
			void ParticleSystem::add_particles_event(Float time, ParticleEvent * event)
			{
				// add event to events list
				add_event_type(event->get_type());
				
				// getting array for current eventy type
				KeyFrameArray * keyFrames = &_keyFrames[event->get_type()];
				
				// try to find keyframe for this time
				KeyFrameIterator itr = keyFrames->begin();
				while(itr != keyFrames->end())
				{
					// check keyframe
					if(fabs((*itr).time - time) <= std::numeric_limits<Float>::epsilon())
					{
						// add event
						if((*itr).event != NULL)
							(*itr).event->release();

						(*itr).event = event->retain();
						
						// exit
						return;
					}
					else if(time < (*itr).time)
					{
						// create new keyframe
						KeyFrame newKeyframe;
						newKeyframe.time = time;
						newKeyframe.event = event->retain();
						
						// and insert it in timeline
						keyFrames->insert(itr, newKeyframe);

						// exit
						return;
					}
					
					// next objects
					++itr;
				}
				
				// crete new keyframe
				KeyFrame newKeyframe;
				newKeyframe.time  = time;
				newKeyframe.event = event->retain();
				
				// and insert it in timeline
				keyFrames->push_back(newKeyframe);
			}
			
			void ParticleSystem::initialize_particle(Particle * particle, const Matrix & emitterMatrix)
			{
				// clear particle first
				particle->clear();
				
				// apply all initializers to the particle
				std::vector<ParticleInitializer*>::iterator itr = _initializers.begin();
				while(itr != _initializers.end())
				{
					// apply current initializer
					(*itr)->initialize_particle(particle);
					
					// next event
					++itr;
				}

				// update position if not parented to emitter
				if(!_parentToEmitter)
					particle->currentState.position = emitterMatrix * particle->currentState.position;
				
				// save particles state
				particle->save_state();
				
				// particle is alive now
				particle->isAlive = true;
			}
			
			size_t ParticleSystem::update_particles(Particle * particles, size_t count, Float delta_time)
			{
				// count of alive particles
				size_t aliveParticles = 0;
				
				// iterate over all particles
				for(size_t i = 0; i < count; ++i)
				{
					// getting current particle
					Particle * current = particles + i;
					
					//if(_activate)
					//{// update it's time
						current->lifeTime += delta_time;
					
						// check if particles alive
						if(_particlesLifetime > 0 && current->lifeTime >= _particlesLifetime)
							current->isAlive = false;
					
						// update particle
					if(current->isAlive)
					{
						++aliveParticles;
						
						// handle all events for this particle
						KeyFrameMap::iterator itr = _keyFrames.begin();
						while(itr != _keyFrames.end())
						{
							// getting array for current eventy type
							KeyFrameArray * keyFrames = &itr->second;
							
							// find previous and next keyframes for event type
							KeyFrameIterator baseKey = find_base_keyframe(keyFrames, current->lifeTime);
							KeyFrameIterator nextKey = find_next_keyframe(keyFrames, current->lifeTime, baseKey);

							// validate base keyframe
							if(baseKey == keyFrames->end() && nextKey != keyFrames->end())
							{
								baseKey   = nextKey;
								nextKey   = keyFrames->end();
							}
							
							// apply event to particle
							if(baseKey != keyFrames->end())
								baseKey->event->update_particle(current, delta_time, baseKey, nextKey, keyFrames);
							
							// next event type
							++itr;
						}
					}
					//}
				}
				
				return aliveParticles;
			}
			
			KeyFrameIterator ParticleSystem::find_base_keyframe(KeyFrameArray * keyFrames, Float time)
			{
				// iterate over all keyframes
				KeyFrameIterator itr = keyFrames->end() - 1;
				while(true)
				{
					// getting keyframe
					KeyFrame * current = &(*itr);
					
					// check timestamp
					if(current->time <= time)
						return itr;
					
					// if we at end
					if(itr == keyFrames->begin())
						return keyFrames->end();
						
					
					// next keyframe
					--itr;
				}
				
				// nothing found
				return keyFrames->end();
			}
			
			KeyFrameIterator ParticleSystem::find_next_keyframe(KeyFrameArray * keyFrames, Float time, KeyFrameIterator baseKey)
			{
				// if base key doesn't exist
				if(baseKey == keyFrames->end())
				{
					// iterate over all keyframes
					KeyFrameIterator itr = keyFrames->begin();
					while(itr != keyFrames->end())
					{
						// getting keyframe
						KeyFrame * current = &(*itr);
						
						// check timestamp
						if(current->time >= time)
							return itr;
						
						// next keyframe
						++itr;
					}
					
					// nothing found
					return keyFrames->end();
				}
				
				// just return next key
				return baseKey + 1;
			}
			
			void ParticleSystem::on_save_nodes(xml::SaverBase * saver) const
			{
				for(size_t i = 0; i < _initializers.size(); i++)
				{
					lib3d::particles::ParticleInitializer * current = _initializers[i];

					if(dynamic_cast<lib3d::particles::OriginInitializer*>(current) != NULL)
						saver->save_node("StartOrigin", current);

					else if(dynamic_cast<lib3d::particles::ColorInitializer*>(current) != NULL)
						saver->save_node("StartColor", current);

					else if(dynamic_cast<lib3d::particles::RotationInitializer*>(current) != NULL)
						saver->save_node("StartAngle", current);

					else if(dynamic_cast<lib3d::particles::AlphaInitializer*>(current) != NULL)
						saver->save_node("StartAlpha", current);

					else if(dynamic_cast<lib3d::particles::SizeInitializer*>(current) != NULL)
						saver->save_node("StartSize", current);
				}

				for(size_t i = 0; i < get_particles_events_count(); i++)
				{
					lib3d::particles::ParticleEvent * current = get_particles_event(i);

					if(dynamic_cast<lib3d::particles::VelocityEvent*>(current) != NULL)
						saver->save_node("VelocityEvent", current);

					else if(dynamic_cast<lib3d::particles::AlphaEvent*>(current) != NULL)
						saver->save_node("AlphaEvent", current);

					else if(dynamic_cast<lib3d::particles::ColorEvent*>(current) != NULL)
						saver->save_node("ColorEvent", current);

					else if(dynamic_cast<lib3d::particles::GravityEvent*>(current) != NULL)
						saver->save_node("GravityEvent", current);

					else if(dynamic_cast<lib3d::particles::SizeEvent*>(current) != NULL)
						saver->save_node("SizeEvent", current);

					else if(dynamic_cast<lib3d::particles::TorqueEvent*>(current) != NULL)
						saver->save_node("TorqueEvent", current);

					else if(dynamic_cast<lib3d::particles::RotationEvent*>(current) != NULL)
						saver->save_node("RotationEvent", current);
				}

				if(_useAtlas)
				{
					for(size_t i = 0; i < _textureFrames.size(); i++)
					{
						FrameTextureCoords current = _textureFrames[i];
						FrameSaver frame(math::Rect<float>(current.minCorner.x, current.minCorner.y, current.maxCorner.x - current.minCorner.x, current.maxCorner.y - current.minCorner.y));
						saver->save_node("FrameRect", &frame);
					}
				}
			}

			void ParticleSystem::on_save_attributes(xml::SaverBase * saver) const
			{
				if(_name != "")
					saver->save_attribute("Name", _name);

				if(_maxParticlesCount > 0)
					saver->save_attribute("MaxParticles", _maxParticlesCount);

				if(_particlesLifetime > 0.0)
					saver->save_attribute("ParticlesLifetime", _particlesLifetime);

				if(_emitterLifetime > 0.0)
					saver->save_attribute("EmitterLifetime", _emitterLifetime);

				if(_emissionRate > 0)
					saver->save_attribute("EmissionRate", _emissionRate);

				if(_emissionCount > 0)
					saver->save_attribute("EmissionCount", _emissionCount);

				if(!_parentToEmitter)
					saver->save_attribute("ParentedToEmitter", _parentToEmitter);

				if(_frameSize != math::Vector2<float>())
					saver->save_attribute("FrameSize", _frameSize);

				if(_frameDelayTime != 0.03)
					saver->save_attribute("FrameDelay", _frameDelayTime);

				if(!_activate)
					saver->save_attribute("Activate", _activate);

				if(_blendMode != hard::BM_ALPHA)
					saver->save_attribute("BlendMode", _blendMode);
	
				saver->save_attribute("Texture", _texturePath);

				if(!_useAtlas)
				{
					size_t frames = _textureFrames.size();
					if(frames)
						saver->save_attribute("Frames", frames);
				}
			}

			bool ParticleSystem::on_node(xml::Provider * provider, const std::string & name)
			{
				// initializers
				// origin initializer
				TryParseInitializer(OriginInitializer,   "StartOrigin");
				
				// size initializer
				TryParseInitializer(SizeInitializer,     "StartSize");
				
				// diffuse color initializer
				TryParseInitializer(ColorInitializer,    "StartColor");
				
				// rotation initializer
				TryParseInitializer(RotationInitializer, "StartAngle");
				
				// transparency initializer
				TryParseInitializer(AlphaInitializer,    "StartAlpha");
				
				// events
				// velocity event
				TryParseEvent(VelocityEvent, "VelocityEvent");
				
				// alpha event
				TryParseEvent(AlphaEvent,    "AlphaEvent");
				
				// color event
				TryParseEvent(ColorEvent,    "ColorEvent");
				
				// gravity event
				TryParseEvent(GravityEvent,  "GravityEvent");
				
				// size event
				TryParseEvent(SizeEvent,     "SizeEvent");
				
				// torque event
				TryParseEvent(TorqueEvent,   "TorqueEvent");
				
				// rotation event
				TryParseEvent(RotationEvent, "RotationEvent");

				// frame rect
				if(name == "FrameRect")
				{
					if(_useAtlas == false)
						_textureFrames.clear();
					
					FrameLoader frameLoader;
					if(provider->parse_object(&frameLoader))
					{
						_useAtlas = true;
						
						FrameTextureCoords newFrame;
						newFrame.minCorner = math::Vector2<float>(frameLoader.rect.x, frameLoader.rect.y);
						newFrame.maxCorner = newFrame.minCorner + math::Vector2<float>(frameLoader.rect.width, frameLoader.rect.height);
						
						_textureFrames.push_back(newFrame);
						
						return true;
					}
				}

				// unknown node
				return false;
			}
			
			bool ParticleSystem::on_attribute(xml::Provider * provider, const std::string & name, const std::string & value)
			{
				// particle system name
				if(name == "Name")
				{
					_name = value;
					return true;
				}
				
				// max particles number
				if(name == "MaxParticles")
					return converter::convert(value, &_maxParticlesCount);
				
				// particles lifetime
				if(name == "ParticlesLifetime")
					return converter::convert(value, &_particlesLifetime);
				
				// emitter lifetime
				if(name == "EmitterLifetime")
					return converter::convert(value, &_emitterLifetime);
				
				// emitter lifetime
				if(name == "EmissionRate")
					return converter::convert(value, &_emissionRate);
				
				// emitter lifetime
				if(name == "EmissionCount")
					return converter::convert(value, &_emissionCount);

				// if particles should be parented to emitter
				if(name == "ParentedToEmitter")
					return converter::convert(value, &_parentToEmitter);
				
				// particles texture
				if(name == "Texture")
				{
					// save path
					_texturePath = value.c_str();
					
					// load start loading
					HardVector::iterator it = hard_vector.begin();
					for(; it != hard_vector.end(); ++it)
					{
						if(it->second == NULL)
							load_texture(it);
					}
					
					// all ok
					return true;
				}
				
				// texture frames count
				if(name == "Frames")
				{
					// read frames number
					unsigned int frames;
					if(!converter::convert(value, &frames))
						return false;
					
					// resize frames vector
					_textureFrames.resize(frames);
					
					// all ok
					return true;
				}
				
				// texture frame size
				if(name == "FrameSize")
					return converter::convert(value, &_frameSize);
				
				// texture frame delay
				if(name == "FrameDelay")
					return converter::convert(value, &_frameDelayTime);

				if(name == "Activate")
					return converter::convert(value, &_activate);

				if (name == "BlendMode")
					return converter::convert(value, &this->_blendMode);

				// unknown attribute
				return false;
			}

			void ParticleSystem::set_particles_texture(const filesystem::String & path)
			{
				_textureFrames.clear();

				_texturePath = path;
					
				// load start loading
				HardVector::iterator it = hard_vector.begin();
				for(; it != hard_vector.end(); ++it)
				{
					if(it->second != NULL)
						delete it->second;

					it->second = NULL;
					
					load_texture(it);
				}
			}

			filesystem::String ParticleSystem::get_particles_texture_path()
			{
				return _texturePath;
			}

			bool ParticleSystem::is_parented_to_emitter() const
			{
				return _parentToEmitter;
			}
		}
	}
}